using System;
using System.Collections.Generic;
using System.Text;
using RapidHDL.Fabric;

namespace RapidHDL
{
    public class FabricComponent : Component 
    {
        ContainerBlock oContainerBlock;

        public Dictionary<string, NodeVector> Inputs;
        public Dictionary<string, NodeVector> Outputs;

        public Dictionary<string, PipelineBus> InputBuses;
        public Dictionary<string, PipelineBus> OutputBuses;

        public Dictionary<string, NodeVector> ControlStates;
        public Dictionary<string, NodeVector> StatusBits;

        ClockComponent oClock;
        NodeVector nvReset;

        public FabricComponent(Component poParentComponent, string psName, ClockComponent poClock, NodeVector pnvReset)
            : base(poParentComponent, psName)
        {
            oClock = poClock;
            nvReset = pnvReset;
            oContainerBlock = new ContainerBlock(psName);
            Inputs = new Dictionary<string, NodeVector>();
            Outputs = new Dictionary<string, NodeVector>();
            InputBuses = new Dictionary<string, PipelineBus>();
            OutputBuses = new Dictionary<string, PipelineBus>();
            ControlStates = new Dictionary<string, NodeVector>();
            StatusBits = new Dictionary<string, NodeVector>();
            BaseDefineFabric();
        }


        public FabricComponent(Component poParentComponent, string psName, ClockComponent poClock, NodeVector pnvReset, bool bBaseDefineFabric)
            : base(poParentComponent, psName)
        {
            oClock = poClock;
            nvReset = pnvReset;
            oContainerBlock = new ContainerBlock(psName);
            Inputs = new Dictionary<string, NodeVector>();
            Outputs = new Dictionary<string, NodeVector>();
            InputBuses = new Dictionary<string, PipelineBus>();
            OutputBuses = new Dictionary<string, PipelineBus>();
            ControlStates = new Dictionary<string, NodeVector>();
            StatusBits = new Dictionary<string, NodeVector>();
            if (bBaseDefineFabric)
                BaseDefineFabric();
        }

        public ContainerBlock ContainerBlock
        {
            get { return oContainerBlock; }
        }

        public void BaseDefineFabric()
        {
            DefineFabric();

            ContainerBlock.BaseGenerateStructure(this,oClock,nvReset);                      

            foreach (ExternalIOBlock oIOBlock in oContainerBlock.ExternalInputs.Values)
            {
                NodeVector nvNodeVector = new NodeVector(this, oIOBlock.Name, oIOBlock.Inputs["IN"].Width);
                oIOBlock.Inputs["IN"].NodeVector.Join(nvNodeVector);
                Inputs.Add(oIOBlock.Name, nvNodeVector);
            }

            foreach (ExternalIOBlock oIOBlock in oContainerBlock.ExternalOutputs.Values)
            {
                NodeVector nvNodeVector = new NodeVector(this, oIOBlock.Name, oIOBlock.Outputs["OUT"].Width);
                oIOBlock.Outputs["OUT"].NodeVector.Join(nvNodeVector);
                Outputs.Add(oIOBlock.Name, nvNodeVector);
            }


            foreach (ExternalBusBlock oBusBlock in oContainerBlock.ExternalBusInputs.Values)
            {
                PipelineBus oBus = new PipelineBus(this, oBusBlock.Name, oBusBlock.Inputs["IN"].Width);
                oBusBlock.Inputs["IN"].NodeVector.Join(oBus.Data);
                oBusBlock.StatusOutputs["STALL"].NodeVector.Join(oBus.Stall);
                oBusBlock.ControlInputs["LATCH"].NodeVector.Join(oBus.DataReady);
                InputBuses.Add(oBusBlock.Name, oBus);
            }

            foreach (ExternalBusBlock oBusBlock in oContainerBlock.ExternalBusOutputs.Values)
            {
                PipelineBus oBus = new PipelineBus(this, oBusBlock.Name, oBusBlock.Outputs["OUT"].Width);
                oBusBlock.Outputs["OUT"].NodeVector.Join(oBus.Data);
                oBusBlock.StatusOutputs["READY"].NodeVector.Join(oBus.DataReady);
                oBusBlock.ControlInputs["READ"].NodeVector.Join(!oBus.Stall);
                
                OutputBuses.Add(oBusBlock.Name, oBus);
            }

            foreach (ControlState oControlState in oContainerBlock.ControlStates.Values)
            {
                NodeVector nvNodeVector = new NodeVector(this, oControlState.Name, 1);
                oControlState.Input.NodeVector.Join(nvNodeVector);
                ControlStates.Add(oControlState.Name, nvNodeVector);
            }

            ContainerBlock.ExpandJoins();

            string sResult = ContainerBlock.ValidateStructure();
            if (sResult != "")
            {
                Console.Write(sResult);
                if (sResult.Contains("ERROR"))
                {
                    throw new Exception(sResult);
                }
            }

        }

        public virtual void DefineFabric()
        {
        }

        public void AddSignalViews()
        {
            oContainerBlock.AddSignalViews();

            foreach (string sOutput in Outputs.Keys)
            {
                RapidHardware.SystemInterface.AddSignalView(sOutput, Outputs[sOutput]);
            }

            foreach (string sOutput in OutputBuses.Keys)
            {
                PipelineBus oBus = OutputBuses[sOutput];
                RapidHardware.SystemInterface.AddSignalView(sOutput + "_DATA", oBus.Data);
                RapidHardware.SystemInterface.AddSignalView(sOutput + "_READY", oBus.DataReady);
                RapidHardware.SystemInterface.AddSignalView(sOutput + "_STALL", oBus.Stall);
            }

        }

        public void AddSignalSources()
        {
            foreach (string sState in ControlStates.Keys)
            {
                RapidHardware.SystemInterface.AddSignalSource(sState, ControlStates[sState], 0);
            }

            foreach (string sInput in Inputs.Keys)
            {
                RapidHardware.SystemInterface.AddSignalSource(sInput, Inputs[sInput], 0);
            }

            foreach (string sInput in InputBuses.Keys)
            {
                PipelineBus oBus = InputBuses[sInput];
                RapidHardware.SystemInterface.AddSignalSource(sInput + "_DATA", oBus.Data, 0);
                RapidHardware.SystemInterface.AddSignalSource(sInput + "_STALL", oBus.Stall, 0);
            }
        }

    }
}
